export type LengthMode = 'char' | 'codePoint' | 'byte'
export type ByteEncoding = 'utf8' | 'gbk'

function utf8ByteLenByCodePoint(cp: number): number {
  if (cp <= 0x7f) return 1
  if (cp <= 0x7ff) return 2
  if (cp <= 0xffff) return 3
  return 4
}

function computeLength(input: string, mode: LengthMode, encoding: ByteEncoding): number {
  switch (mode) {
    case 'char':
      return input.length
    case 'codePoint':
      return Array.from(input).length
    case 'byte': {
      if (encoding === 'gbk') {
        let sum = 0
        for (const ch of input) {
          sum += ch.charCodeAt(0) <= 0x7f ? 1 : 2
        }
        return sum
      } else {
        let sum = 0
        for (const ch of Array.from(input)) {
          const cp = ch.codePointAt(0)!
          sum += utf8ByteLenByCodePoint(cp)
        }
        return sum
      }
    }
    default:
      return input.length
  }
}

export interface LengthOptions {
  min?: number
  max?: number
  trim?: boolean
  mode?: LengthMode        // 默认 'codePoint'
  encoding?: ByteEncoding  // mode 为 'byte' 时有效，默认 'utf8'
  fieldName?: string       // 生成友好提示
}

export interface LengthResult {
  valid: boolean
  length: number
  message?: string
}

function suffix(mode: LengthMode, encoding: ByteEncoding): string {
  if (mode === 'byte') return `字节(${encoding.toUpperCase()})`
  if (mode === 'codePoint') return '字符'
  return '字符'
}

/**
 * 生成详细校验结果与提示
 */
export function validateLength(
  value: string | null | undefined,
  options: LengthOptions = {}
): LengthResult {
  const {
    min,
    max,
    trim = true,
    mode = 'codePoint',
    encoding = 'utf8',
    fieldName = '内容',
  } = options

  let v = value ?? ''
  if (trim) v = v.trim()

  const len = computeLength(v, mode, encoding)

  if (min != null && len < min) {
    return {
      valid: false,
      length: len,
      message: `${fieldName}长度不能小于${min}${suffix(mode, encoding)}`,
    }
  }
  if (max != null && len > max) {
    return {
      valid: false,
      length: len,
      message: `${fieldName}长度不能超过${max}${suffix(mode, encoding)}`,
    }
  }
  return { valid: true, length: len }
}

/**
 * 简化判断：长度区间
 */
export function isLengthBetween(
  value: string | null | undefined,
  min?: number,
  max?: number,
  opts?: Omit<LengthOptions, 'min' | 'max'>
): boolean {
  const res = validateLength(value, { ...opts, min, max })
  return res.valid
}

/**
 * 适配 Element Plus 的表单规则
 * 用法：rules: { username: [{ validator: makeElLengthRule({ min:3, max:16, fieldName:'用户名' }), trigger:'blur' }] }
 */
export function makeElLengthRule(opts: LengthOptions) {
  return (_rule: any, value: any, callback: (err?: Error) => void) => {
    const { valid, message } = validateLength(String(value ?? ''), opts)
    if (valid) return callback()
    callback(new Error(message || '长度不符合要求'))
  }
}
